import { uim } from './UIManager';
import { BaseView } from './../rxjs/cc3/BaseView';
import { _decorator, Node, assert, Prefab, isValid, instantiate, find, color } from 'cc';
import { concat, map, Subscription, Observable, of, defer, forkJoin } from 'rxjs';
import { bundleLoad, bundleRelease } from '../rxjs/cc3/AssetManagerLoad';
import { BasePanel } from './BasePanel';
import { PanelKey, UIConfig } from './UIConfig';
const { ccclass, property } = _decorator;

@ccclass('BaseUIRoot')
export class BaseUIRoot extends BaseView {

    @property
    key = 'lobby';

    get logColorOverride() { return color().fromHEX('#CCDDEE') };

    willBind() {
        if (globalThis.BaseUIRoot == null) globalThis.BaseUIRoot = this;
    }

    useObserves() {
        return [
            uim.registerInitWithScenePanelCommand.subscribe(_ => {
                const { panelKey, node } = _;
                this._panelNodes[panelKey] = node;
            }),
        ];
    }

    // panelChanged(panelKey: PanelKey, value: boolean) {
    //     this.log('panelChanged', panelKey, value);
    //     value ? this.openPanel(panelKey).subscribe() : this.closePanel(panelKey).subscribe();
    // }

    getParent(panelKey) {
        const panelConfigInfo = UIConfig.get(this.key).get(panelKey);
        if (panelConfigInfo.tier == null) return this.node;
        const parent = find(panelConfigInfo.tier, this.node);
        assert(!!parent, `找不到tier,${panelKey}, ${panelConfigInfo.tier}`);
        return parent;
    }

    getPanelKeyEnum() {
        return PanelKey;
    }

    stringToPanelKey(str) {
        return this.getPanelKeyEnum()[str];
    }

    _panelNodes = new Map<PanelKey, Node>();
    _panel_subs = new Map<PanelKey, Subscription>();
    openPanel(panelKey: PanelKey): Observable<{ panelKey: PanelKey, _: any }> {
        return new Observable(observer => {
            this.log('openPanel', panelKey, PanelKey[panelKey]);
            this._panel_subs.get(panelKey)?.unsubscribe();
            this._panel_subs.delete(panelKey);
            const panelConfigInfo = UIConfig.get(this.key).get(panelKey);
            let panelNode = this._panelNodes.get(panelKey);
            let panel: BasePanel = null;
            const obs = [];
            if (!isValid(panelNode)) {
                if (panelConfigInfo.scene) {
                    let parent = this.getParent(panelKey);
                    panelNode = find(panelConfigInfo.name, parent);
                    assert(isValid(panelNode), `❗标记了随场景带入，但是没有在场景的指定位置, ${panelKey}, ${PanelKey[panelKey]}`);
                    this._panelNodes.set(panelKey, panelNode);
                    panelNode.getComponent(BasePanel).initAnim();
                    obs.push(of('init'));
                } else {
                    obs.push(bundleLoad(panelConfigInfo.assetbundle, panelConfigInfo.assetPath, Prefab).pipe(map(_ => {
                        panelNode = instantiate(_.data as any);
                        panelNode.setParent(this.getParent(panelKey));
                        this._panelNodes.set(panelKey, panelNode);
                        panelNode.getComponent(BasePanel).initAnim();
                        return 'init';
                    })))
                }
            }
            obs.push(defer(() => of(null).pipe(map(_ => {
                panel = panelNode.getComponent(BasePanel)
                assert(!!panel, `❗这个prefab不是BasePanel或继承BasePanel, ${panelKey}, ${PanelKey[panelKey]}`);
                return 'check';
            }))));
            obs.push(defer(() => panel.willOpen()));
            obs.push(defer(() => panel.open()));
            obs.push(defer(() => panel.afterOpen()));
            const sub = concat(...obs).subscribe({
                next: _ => observer.next({ panelKey, _ }),
                complete: () => observer.complete()
            });
            this._panel_subs.set(panelKey, sub);
            return () => {
                sub.unsubscribe();
                this._panel_subs.delete(panelKey);
            }
        });
    }

    closePanel(panelKey: PanelKey): Observable<{ panelKey: PanelKey, _: any }> {
        return new Observable(observer => {
            this.log('closePanel', panelKey, PanelKey[panelKey]);
            this._panel_subs.get(panelKey)?.unsubscribe();
            this._panel_subs.delete(panelKey);
            const panelConfigInfo = UIConfig.get(this.key).get(panelKey);
            let panelNode = this._panelNodes.get(panelKey);
            if (!isValid(panelNode)) return;
            const panel = panelNode.getComponent(BasePanel);
            const obs = [];
            obs.push(panel.willClose());
            obs.push(panel.close());
            obs.push(panel.afterClose());
            obs.push(of(null).pipe(map(_ => {
                if (panelConfigInfo.destroy) {
                    panelNode.destroy();
                    this._panelNodes.delete(panelKey);
                    bundleRelease(panelConfigInfo.assetbundle, panelConfigInfo.assetPath, Prefab);
                }
                return 'destroy';
            })));
            const sub = concat(...obs).subscribe({
                next: _ => observer.next({ panelKey, _ }),
                complete: () => observer.complete()
            });
            this._panel_subs.set(panelKey, sub);
            return () => {
                sub.unsubscribe();
                this._panel_subs.delete(panelKey);
            }
        });
    }

    // 这里面的subscribe是不用中断的。在每个panel的open和close里面自己处理了中断取消。
    // 因此，在连续调用flowStatus时，其中每个受控的panel状态不会出问题。总是以最后一次的flowStatus为准。
    statusChangedWithConfig(pair, config) {
        this.log('@statusChangedWithConfig', pair, config);
        let panelKeySet0 = config.get(pair[0]);
        let panelKeySet1 = config.get(pair[1]);
        let obs: Observable<any>[] = [];
        panelKeySet0.forEach(_ => {
            if (!panelKeySet1.has(_)) {
                obs.push(this.closePanel(_).pipe(map(_ => {
                    uim.onPanelChangeCommand.execute(_);
                    return _;
                })));
            }
        });
        panelKeySet1.forEach(_ => {
            if (!panelKeySet0.has(_)) {
                obs.push(this.openPanel(_).pipe(map(_ => {
                    uim.onPanelChangeCommand.execute(_);
                    return _;
                })));
            }
        });
        if (obs.length > 0) {
            uim.standby.value = false;
            forkJoin(obs).subscribe({
                complete: () => uim.standby.value = true
            });
        }
    }
}


